package lin

import (
	"database/sql"
	"errors"
	"fmt"
	"strings"

	//"fmt"
	"time"
)

// 使用要应用 对应的 驱动引擎
// _ "github.com/mattn/go-sqlite3"
// _ "github.com/denisenkom/go-mssqldb"
// _"github.com/godror/godror"  oracledror?
// _ "github.com/go-sql-driver/mysql"
// _ "github.com/mattn/go-adodb"  //sql2000驱动
type S数据库_键值列表 struct {
	链接      *sql.DB
	最大时长    int
	最大连接数   int
	空闲连接数   int
	是否oracl bool
}

// 初始化配置 连接前设置
// @ 最大时长 单位 为分钟
func (类 *S数据库_键值列表) ZP置配置(最大时长, 最大连接数, 空闲连接数 int) {
	类.最大时长 = 最大时长
	类.最大连接数 = 最大连接数
	类.空闲连接数 = 空闲连接数
}
func (类 *S数据库_键值列表) ZX置心跳时间(执行间隔_秒 int) {
	go 数据库心跳(类.链接, 执行间隔_秒)
}

func 数据库心跳(来源 *sql.DB, 执行间隔秒 int) {
	间隔 := 执行间隔秒 * 1000
	for {
		全_时间.YC延迟程序(间隔)
		if err := 来源.Ping(); err != nil {
			fmt.Println("数据库断开:::", err.Error())
			return
		}
	}
}

func (类 *S数据库_键值列表) 执行配置() {
	if 类.最大时长 != 0 {
		类.链接.SetConnMaxLifetime(time.Minute * time.Duration(类.最大时长))
	}
	if 类.最大连接数 != 0 {
		类.链接.SetMaxOpenConns(类.最大连接数)
	}
	if 类.空闲连接数 != 0 {
		类.链接.SetMaxIdleConns(类.空闲连接数)
	}

}

func (类 *S数据库_键值列表) CJ创建链接_mssql(地址, 端口, 库名, 账号, 密码 string) (返回_错误 error) {
	connectString := "server=" + 地址 + ";database=" + 库名 + ";user id=" + 账号 + ";password=" + 密码 + ";port=" + 端口 + ";encrypt=disable"
	类.链接, 返回_错误 = sql.Open("mssql", connectString)
	if 返回_错误 != nil {
		return
	}
	类.执行配置()
	_, 返回_错误 = 类.链接.Exec("select name from SYSOBJECTS")
	return
}

func (类 *S数据库_键值列表) CJ创建链接_mssql_2000(地址, 端口, 库名, 账号, 密码 string) (返回_错误 error) {
	connectString := "Provider=SQLOLEDB;Initial Catalog=" + 库名 + ";Data Source=" + 地址 + "," + 端口 + ";user id=" + 账号 + ";password=" + 密码
	类.链接, 返回_错误 = sql.Open("adodb", connectString)
	if 返回_错误 != nil {
		return
	}
	类.执行配置()
	_, 返回_错误 = 类.链接.Exec("select name from SYSOBJECTS")
	return
}

func (类 *S数据库_键值列表) CJ创建链接_oracl(用户名, 密码, 连接库地址 string) (返回_错误 error) {
	connectString := "user=" + 用户名 + " password=" + 密码 + " connectString=" + 连接库地址
	类.链接, 返回_错误 = sql.Open("godror", connectString)
	if 返回_错误 != nil {
		return
	}
	类.是否oracl = true
	类.执行配置()
	_, 返回_错误 = 类.链接.Exec("select * from user_tables")
	return
}

func (类 *S数据库_键值列表) CJ创建链接_sqlite3(地址 string) (返回_错误 error) {
	类.链接, 返回_错误 = sql.Open("sqlite3", 地址)
	if 返回_错误 != nil {
		return
	}
	类.执行配置()
	_, 返回_错误 = 类.链接.Exec("SELECT name from sqlite_master where type='table'")
	return
}

func (类 *S数据库_键值列表) CJ创建链接_mysql(地址, 端口, 库名, 账号, 密码 string) (返回_错误 error) {
	connectString := 账号 + ":" + 密码 + "@tcp(" + 地址 + ":" + 端口 + ")/" + 库名
	类.链接, 返回_错误 = sql.Open("mysql", connectString)
	if 返回_错误 != nil {
		return
	}
	类.执行配置()
	_, 返回_错误 = 类.链接.Query("SHOW TABLES")
	return
}
func (类 *S数据库_键值列表) C_查(sql语句 string, 防止注入 ...bool) (返回_列表 L列表, 返回_错误 error) {
	if len(防止注入) > 0 && 防止注入[0] {
		判断注入文本 := strings.ToLower(sql语句)
		if strings.Contains(判断注入文本, "delete") {
			返回_错误 = errors.New("不能包含关键字 delete")
			return
		}
		if strings.Contains(判断注入文本, "insert") {
			返回_错误 = errors.New("不能包含关键字 insert")
			return
		}
		if strings.Contains(判断注入文本, "update") {
			返回_错误 = errors.New("不能包含关键字 insert")
			return
		}

	}

	是否oracl := 类.是否oracl
	返回_列表.Q清空()
	rows, err := 类.链接.Query(sql语句)
	if err != nil {
		返回_错误 = err
		return
	}
	defer rows.Close()
	键组, _ := rows.Columns()
	cache := make([]any, len(键组))
	for index := range cache {
		var a any
		cache[index] = &a
	}
	for rows.Next() {
		_ = rows.Scan(cache...)
		item := make(map[string]any)
		for i, data := range cache {
			值 := *data.(*any)
			if 是否oracl {
				返回_错误 = 键值列表_类型筛选(值)
				if 返回_错误 != nil {
					值 = any_到文本(值)
				}
			}
			item[键组[i]] = 值
			//item[键组[i]] = *data.(*any) //any中是什么 就返回什么
		}
		返回_错误 = 返回_列表.T添加值(item)
		if 返回_错误 != nil {
			return
		}
	}

	return
}
func (类 *S数据库_键值列表) Z_增(库表名 string, 插入键值表 J键值表) (返回_错误 error) {
	map表 := 插入键值表.D到map()
	键组 := make([]string, 0)
	值组 := make([]any, 0)
	替代组 := make([]string, 0)
	i := 0
	for 键, 值 := range map表 {
		switch 当前值 := 值.(type) {
		case nil:
			continue
		case J键值表:
			文本 := 当前值.D到JSON()
			值组 = append(值组, 文本)
		case L列表:
			文本 := 当前值.D到JSON()
			值组 = append(值组, 文本)
		case []any:
			值组 = append(值组, any_到文本(当前值))
		case map[string]any:
			值组 = append(值组, any_到文本(当前值))
		default:
			值组 = append(值组, 当前值)
		}
		键组 = append(键组, 键)
		替代组 = append(替代组, "?")
		i++
	}
	if len(键组) == 0 {
		返回_错误 = errors.New("错误:天加参数不能为空")
		return
	}
	sqlStr := "INSERT INTO " + 库表名 + " (" + strings.Join(键组, ",") + ")" + "VALUES (" + strings.Join(替代组, ",") + ")"
	_, 返回_错误 = 类.链接.Exec(sqlStr, 值组...)
	return
}

func (类 *S数据库_键值列表) G_改(库表名 string, 修改键值表 J键值表, 条件 ...string) (返回_影响行数 int, 返回_错误 error) {
	map表 := 修改键值表.D到map()
	键组 := make([]string, 0)
	值组 := make([]any, 0)
	是否oracl := 类.是否oracl

	i := 0
	for 键, 值 := range map表 {
		switch 当前值 := 值.(type) {
		case nil:
			continue
		case J键值表:
			文本 := 当前值.D到JSON()
			值组 = append(值组, 文本)
		case L列表:
			文本 := 当前值.D到JSON()
			值组 = append(值组, 文本)
		case []any:
			值组 = append(值组, any_到文本(当前值))
		case map[string]any:
			值组 = append(值组, any_到文本(当前值))
		default:
			值组 = append(值组, 当前值)
		}
		if 是否oracl {
			键组 = append(键组, 键+"=:"+键)
		} else {
			键组 = append(键组, 键+"=?")
		}
		i++
	}
	sqlStr := ""
	if len(条件) >= 1 && 条件[0] != "" {
		sqlStr = "UPDATE " + 库表名 + " SET " + strings.Join(键组, ",") + " WHERE " + 条件[0]
	} else {
		sqlStr = "UPDATE " + 库表名 + " SET " + strings.Join(键组, ",")
	}

	ref, err := 类.链接.Exec(sqlStr, 值组...)
	if err != nil {
		返回_错误 = err
		return
	}
	n, _ := ref.RowsAffected()
	返回_影响行数 = int(n)
	return
}

func (类 *S数据库_键值列表) S_删(库表名 string, 条件 ...string) (返回_影响行数 int, 返回_错误 error) {

	sqlStr := ""
	if len(条件) >= 1 && 条件[0] != "" {
		sqlStr = "DELETE FROM  " + 库表名 + " WHERE " + 条件[0]
	} else {
		sqlStr = "DELETE FROM  " + 库表名
	}

	ref, err := 类.链接.Exec(sqlStr)
	if err != nil {
		返回_错误 = err
		return
	}
	n, _ := ref.RowsAffected()
	返回_影响行数 = int(n)
	return
}
func (类 *S数据库_键值列表) ZX执行sql(sql语句 string) (返回_影响行数 int, 返回_错误 error) {
	ref, err := 类.链接.Exec(sql语句)
	if err != nil {
		返回_错误 = err
		return
	}
	n, _ := ref.RowsAffected()
	返回_影响行数 = int(n)
	return
}

// 本对象 只能执行一次事务  S_事务提交 或 S_事务回滚 后 失效
func (类 *S数据库_键值列表) QS取事务对象() (事务对象 S数据库_键值列表_事务对象, 返回_错误 error) {
	事务句柄, 返回_错误 := 类.链接.Begin()
	事务对象.c_初始化赋值事务句柄(事务句柄, 类.是否oracl)
	return
}

type S数据库_键值列表_事务对象 struct {
	事务句柄    *sql.Tx
	是否oracl bool
}

func (类 *S数据库_键值列表_事务对象) c_初始化赋值事务句柄(事务句柄 *sql.Tx, 是否oracl bool) {
	类.事务句柄 = 事务句柄
	类.是否oracl = 是否oracl
}
func (类 *S数据库_键值列表_事务对象) S事务_提交() (返回_错误 error) {
	返回_错误 = 类.事务句柄.Commit()
	return
}
func (类 *S数据库_键值列表_事务对象) S事务_回滚() (返回_错误 error) {
	返回_错误 = 类.事务句柄.Rollback()
	return
}

func (类 *S数据库_键值列表_事务对象) Z_增(库表名 string, 插入键值表 J键值表) (返回_错误 error) {
	map表 := 插入键值表.D到map()
	键组 := make([]string, 0)
	值组 := make([]any, 0)
	替代组 := make([]string, 0)
	i := 0
	for 键, 值 := range map表 {
		switch 当前值 := 值.(type) {
		case nil:
			continue
		case J键值表:
			文本 := 当前值.D到JSON()
			值组 = append(值组, 文本)
		case L列表:
			文本 := 当前值.D到JSON()
			值组 = append(值组, 文本)
		case []any:
			值组 = append(值组, any_到文本(当前值))
		case map[string]any:
			值组 = append(值组, any_到文本(当前值))
		default:
			值组 = append(值组, 当前值)
		}
		键组 = append(键组, 键)
		替代组 = append(替代组, "?")
		i++
	}
	if len(键组) == 0 {
		返回_错误 = errors.New("错误:天加参数不能为空")
		return
	}
	sqlStr := "INSERT INTO " + 库表名 + " (" + strings.Join(键组, ",") + ")" + "VALUES (" + strings.Join(替代组, ",") + ")"
	_, 返回_错误 = 类.事务句柄.Exec(sqlStr, 值组...)
	return
}

func (类 *S数据库_键值列表_事务对象) G_改(库表名 string, 修改键值表 J键值表, 条件 ...string) (返回_影响行数 int, 返回_错误 error) {
	map表 := 修改键值表.D到map()
	键组 := make([]string, 0)
	值组 := make([]any, 0)
	是否oracl := 类.是否oracl
	i := 0
	for 键, 值 := range map表 {
		switch 当前值 := 值.(type) {
		case nil:
			continue
		case J键值表:
			文本 := 当前值.D到JSON()
			值组 = append(值组, 文本)
		case L列表:
			文本 := 当前值.D到JSON()
			值组 = append(值组, 文本)
		case []any:
			值组 = append(值组, any_到文本(当前值))
		case map[string]any:
			值组 = append(值组, any_到文本(当前值))
		default:
			值组 = append(值组, 当前值)
		}
		if 是否oracl {
			键组 = append(键组, 键+"=:"+键)
		} else {
			键组 = append(键组, 键+"=?")
		}
		i++
	}
	sqlStr := ""
	if len(条件) >= 1 && 条件[0] != "" {
		sqlStr = "UPDATE " + 库表名 + " SET " + strings.Join(键组, ",") + " WHERE " + 条件[0]
	} else {
		sqlStr = "UPDATE " + 库表名 + " SET " + strings.Join(键组, ",")
	}

	ref, err := 类.事务句柄.Exec(sqlStr, 值组...)
	if err != nil {
		返回_错误 = err
		return
	}
	n, _ := ref.RowsAffected()
	返回_影响行数 = int(n)
	return
}

func (类 *S数据库_键值列表_事务对象) S_删(库表名 string, 条件 ...string) (返回_影响行数 int, 返回_错误 error) {

	sqlStr := ""
	if len(条件) >= 1 && 条件[0] != "" {
		sqlStr = "DELETE FROM  " + 库表名 + " WHERE " + 条件[0]
	} else {
		sqlStr = "DELETE FROM  " + 库表名
	}

	ref, err := 类.事务句柄.Exec(sqlStr)
	if err != nil {
		返回_错误 = err
		return
	}
	n, _ := ref.RowsAffected()
	返回_影响行数 = int(n)
	return
}

func (类 *S数据库_键值列表_事务对象) ZX_执行sql(sql语句 string) (返回_影响行数 int, 返回_错误 error) {
	ref, err := 类.事务句柄.Exec(sql语句)
	if err != nil {
		返回_错误 = err
		return
	}
	n, _ := ref.RowsAffected()
	返回_影响行数 = int(n)
	return
}
